import {newArrayProperty} from "./array.js";
import Dep from "./dep.js";

class Observer{
    constructor(data) {
        // 给对象类型的数据加一个 Dep 实例
        this.dep = new Dep()
        // 定义一个__ob__，值是this，不可枚举
        // 给数据加了一个标识，表示这个数据是一个已经被响应式了的
        Object.defineProperty(data,"__ob__",{
            // 定义这个属性值是当前的实例
            value:this,
            // 定义__ob__不能被遍历，否则会引起死循环
            // 原因：在walk方法中会递归遍历对象中的每一个属性进行响应式处理，因为__ob__表示的当前对象的实例
            // 实例本身又包含__ob__，这样就会导致递归无限往里面找，就造成了死循环，
            // 所以这里要设置成 enumerable：false，不能遍历 __ob__ 这个属性
            enumerable:false
        })
        // 如果data中的某个值定义的是一个数组，则对数组进行劫持，进行响应式处理
        if(Array.isArray(data)){
            data.__proto__ = newArrayProperty
            this.observeArray(data)
        }else{
            this.walk(data)
        }
    }
    walk(data){
        // 循环对象中的每一个属性进行劫持
        Object.keys(data).forEach(key=>{
            defineReactive(data,key,data[key])
        })
    }
    // 对数组进行响应式处理
    observeArray(data){
        data.forEach(item=>observe(item))
    }
}

function dependArr(array){
    array.forEach(item=>{
        // 数组中的普通类型的值不会有__ob__
        item.__ob__ && item.__ob__.dep.depend()
        if(Array.isArray(item)){
            dependArr(item)
        }
    })
}

export function defineReactive(target,key,value){
    // 这里对当前的值进行判断，如果值还是一个对象，则递归继续进行深度劫持
    const childOb = observe(value)
    // Object.defineProperty 接收三个参数，第一个是对象，第二是要劫持的key，第三个是一个对象，里面有get和set方法
    // 当读取这个key是，会触发get方法，当设置key时，会触发set方法，并接收新值
    let dep = new Dep();
    Object.defineProperty(target,key,{
        get(){
            // 进行依赖收集，收集这个属性的观察者（watcher）
            if(Dep.target){
                dep.depend()
                if(childOb){
                    childOb.dep.depend()
                    if(Array.isArray(value)){
                        dependArr(value)
                    }
                }
            }
            return value
        },
        set(newValue){
            if(newValue === value) return
            value = newValue
            // 通知观察者更新视图
            dep.notify()
        }
    })
}


export function observe(data){
    // 对这个对象进行劫持，需要判断一下是否是一个对象，如果不是一个对象不能进行劫持
    if(typeof data !== "object" || data === null) return
    // 如果这个对象已经被代理过了，则直接返回当前示例
    if(data.__ob__){
        return data.__ob__
    }
    return new Observer(data)
}